/* -*- Mode:Prolog; coding:ISO-8859-1; -*- */
:- multifile test/2.
:- discontiguous test/2.


%@ build_solution(+S, -Out)
% Given an abductive solution S for the learning problem it returns
% a list of rules representing the final hypothesis Out 
build_solution(S, Out) :- 
        find_rules(S, SS), 
        xbuild_solution(SS,  Out), !.


%@ find_rules(+S, -Out)
% Given an abductive solution S for the learning problem it returns
% a list of elements rule(H, O) where H represents the rule and O
% are the links for the output in the head.
find_rules(S , O) :- 
        findall(H, 
                (
                   last_rule(H,  S)
                ),
                O), !.

find_rules(_S , []).

last_rule(rule(H, OutsH), S) :-
        member(r(_Level, H, OutsH), S).

last_rule(rule(H, []), S) :-
        member(pr(Level, H), S), 
        no_bigger(Level, H, S).

no_bigger(_L, _H, []).
no_bigger(Level, H, [pr(Level2, SH) | _ST]):-
        append(H, L, SH), (L= [], Level < Level2; L\= []), !, fail.
no_bigger(_, H, [r(_, H, _) | _ST]):-
         !, fail.
no_bigger(Level, H, [_SH | ST]):-
        no_bigger(Level, H, ST).





xbuild_solution([], []).

xbuild_solution([H | T], [OutH | Out]) :- 
        build_rule(H, OutHT),                   %This gives OutH that is the rule in list form
        out_list_to_clause(OutHT, OutH),        %Produces the rule in clausal form
        xbuild_solution(T, Out).

%Expected
%get_all_vars([(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])], X), generate_variable_index(X, Y).
%X = [(1,1),(1,2)],
%Y = [(1,1)-_A,(1,2)-_B]
%get_all_vars([(r1, [], []), (r2, [], [(1, 1)]), (r3, [], [(1, 1)])], X), get_variables_in_the_outputs([(1, 2),(2, 1)], Y), un(X, Y, Z), generate_variable_index(Z, O).
%O = [(1,1)-_A,(1,2)-_B,(2,1)-_C],
%X = [(1,1)],
%Y = [(1,2),(2,1)],
%Z = [(1,1),(1,2),(2,1)] 
%generate_variable_index([(1,1),(1,2),(2,1)] , X).
%X = [(1,1)-_A,(1,2)-_B,(2,1)-_C] 
%Test
%build_rule(rule([(r1, [], []), (r2, [], [(1, 1)]), (r3, [], [(1, 1)])], [(1, 2),(2, 1)]), NewRule)
build_rule(rule(H, HOuts), NewRule) :- 
        get_all_vars(H,  Variables),              %This produces the list of unbounded 
        get_variables_in_the_outputs(HOuts, VariablesO),
        un(Variables, VariablesO, V),
        generate_variable_index(V, VI),
        build_rule(H, HOuts, VI, NewRule).           %variables of the same number of the variables used in the solution


generate_variable_index([], []).
generate_variable_index([H | T], [H-_ | TR]) :-
        generate_variable_index(T, TR).



%get_all_vars([(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])], X).
get_all_vars([], []).
get_all_vars([(_Mode, _Constants, VarList) | T], L) :-
        get_all_vars(T, LT),
        un(VarList, LT, L).
      
%get_variables_in_the_outputs([(1, 2),(2, 1)], X).
get_variables_in_the_outputs([], []).
get_variables_in_the_outputs([H | T], VariablesO) :- 
        get_variables_in_the_outputs(T, Old),
        un([H], Old, VariablesO).

out_list_to_clause([ H | B], (H :- Conj)) :-  
        list2conjunction(B, Conj).

/*
assert(modename(r1, modeh(penguin(+bird)))),
assert(modename(r2, modeb(eats(+bird, -animal)))),
assert(modename(r3, modeb(hates(+animal, -bird)))), 
build_rule([(r1, [], []), (r2, [], [(1, 1)]), (r3, [], [(1, 1)])], [(1, 2),(2, 1)], [(1,1)-A,(1,2)-B,(2,1)-C], NewRule).  
*/
build_rule([Head | Body], HOuts, VI, NewRule) :-
        build_rule([Head | Body], HOuts, VI, NewRule, h).


build_rule([(Mode, Constants, []) | Body], HOuts, VI, [MadeHead | MadeBody], h) :-
        (modename(Mode, modeh(ModeSchema)), ! %modename(m1, mother_and_pet(+person, -person, -pet ))
        ;
        modename(Mode, modeh(ModeSchema, _))), %modename(m1, mother_and_pet(+person, -person, -pet ))
        dissection(ModeSchema, AllArguments, DissectionedLiteral),      %dissection(penguin(+bird),[+bird],[penguin,+bird]) 
        generate_vars_and_type_check(AllArguments, _InputVars, _OutputVars, Constants, AllVars, TCheckIn, _TCheckC, _TCheckO), %generate_vars_and_type_check([+bird],[_134615],[],[],[_134615],[bird(_134615)],[],[]) ? 
        make_output_list([], TCheckIn, OutputList),     %make_output_list([],[bird(_134615)],[[bird,_134615]]) 
        match_variables(OutputList, VI),
        variabilise(DissectionedLiteral, AllVars, VariabilisedDissectionedLiteral),   %variablise([penguin,+bird], [X], [penguin,X])
        make_literal(VariabilisedDissectionedLiteral, MadeHead), !,       %make_literal([penguin,X], penguin(X)),
        build_rule(Body, HOuts, VI, MadeBody, b, OutputList).


match_variables(OutputList, VI) :-
        match_variables(OutputList  , VI, 1).


match_variables([], _VI, _MM).

match_variables([[_Type | H] | OutputList]  , VI, M) :-
        x_match_variables(H, VI, M, 1),
        MM is M + 1,
        match_variables(OutputList, VI, MM).      

x_match_variables([], _VI, _M, _N) .

x_match_variables([H | OutputListThisType], VI, M, N) :-
        member((M,N)-H, VI), !,
        NN is N+1,
        x_match_variables(OutputListThisType, VI, M, NN). 
        
%Some outputs may not be in VI, never used
x_match_variables([_H | OutputListThisType], VI, M, N) :-
        NN is N+1,
        x_match_variables(OutputListThisType, VI, M, NN). 


build_rule([], _HOuts, _VI, [], b, _OutputList).

build_rule([(Mode, Constants, Links) | TBody], HOuts, VI, [MHBody | MTBody], b, OutputList) :-
        (modename(Mode, modeb(ModeSchema)),! %modename(m1, mother_and_pet(+person, -person, -pet ))
         ;
         modename(Mode, modeb(ModeSchema, _))
        ), 
        dissection(ModeSchema, AllArguments, DissectionedLiteral),      %dissection(penguin(+bird),[+bird],[penguin,+bird])
        generate_vars_and_type_check(AllArguments, InputVars, _OutputVars, Constants, AllVars, _TCheckIn, _TCheckC, TCheckOut), %generate_vars_and_type_check([+bird],[_134615],[],[],[_134615],[bird(_134615)],[],[]) ?
        make_output_list(OutputList, TCheckOut, NewOutputList),     %make_output_list([],[bird(_134615)],[[bird,_134615]])
        match_variables(OutputList, VI),
        match_var_link(Links,InputVars, VI),
        variabilise(DissectionedLiteral, AllVars, VariabilisedDissectionedLiteral),   %variablise([penguin,+bird], [X], [penguin,X])
        make_literal(VariabilisedDissectionedLiteral, MHBody), !,        %make_literal([penguin,X], penguin(X)),
        build_rule(TBody, HOuts, VI, MTBody, b, NewOutputList).


match_var_link([], [], _VI).

match_var_link([H | Links], [HIV | InputVars], VI) :-
        match_var(H, HIV, VI),
        match_var_link(Links, InputVars, VI).

%Expected
%linkx([[person, A, B, C], [pet, D, E]], [X-(1,1),Y-(1,2),W-(1,3),Z-(2,1),XZ-(2,2) ])
%Test
%linkx([[person, A, B, C], [pet, D, E]], X)
linkx(ListOfCurrentOutputs, VI) :- 
        linkx(ListOfCurrentOutputs, VI, 1). 

linkx([], _Dic, _N).
        
linkx([[_ | ListOfCurrentOutputs] | Others], Dic, N) :- 
        linkxx( ListOfCurrentOutputs, Dic, N, 1), 
        NN is N + 1,
        linkx([[_ | ListOfCurrentOutputs] | Others], Dic, NN).

linkxx( [], _Dic, _N, _M).
linkxx( [H | T], Dic, N, M) :-
        match_var((N,M), H, Dic),
        MM is M + 1,
        linkxx( T, Dic, N, MM).
       
match_var((N,M), H, Dic) :-
        member((N,M)-H, Dic).
        
%%%%%%%%%%TESTS




 test(find_rules/2, Outcome) :-
                 ((find_rules([r(3,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])],[]), 
                                pr(3,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])]),
                                pr(2,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])]),
                                pr(1,[(eh,[],[]),(1,[],[(1,1)])]),
                                pr(0,[(eh,[],[])]) ,
                                pr(1,[(oh,[],[]),(1,[],[(1,1)])]),
                                pr(0,[(oh,[],[])]) ], O) ,
                 member(rule([(oh,[],[]),(1,[],[(1,1)])],[]), O),
                 member(rule([(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])],[]), O)
                  )
                  -> 
                        Outcome = true ; Outcome = false).



 test(last_rule/2, Outcome) :-
                 (last_rule(rule(3,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])],[]), 
                              [r(3,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])],[]), 
                                pr(3,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])]),
                                r(3,[(oh,[],[]),(1,[],[(1,1)]),(eb,[],[(1,2)])],[]), 
                                pr(3,[(oh,[],[]),(1,[],[(1,1)]),(eb,[],[(1,2)])]),
                                pr(2,[(eh,[],[]),(1,[],[(1,1)]),(ob,[],[(1,2)])]),
                                pr(1,[(eh,[],[]),(1,[],[(1,1)])]),
                                pr(0,[(eh,[],[])]) ,
                                pr(2,[(oh,[],[]),(1,[],[(1,1)]),(eb,[],[(1,2)])]),
                                pr(1,[(oh,[],[]),(1,[],[(1,1)])]),
                                pr(0,[(oh,[],[])]) ])                 
                  -> 
                        Outcome = true ; Outcome = false).
